﻿using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Platform.Storage;

namespace Bread.Mvc.Avalonia;

/// <summary>
/// 文件过滤描述符
/// </summary>
/// <param name="Name">显示在系统文件管理器的过滤标题，例如： Adobe Premiere Pro Xml 序列 （*.xml）</param>
/// <param name="Exts">实际需要过滤的文件格式, 例如 *.xml </param>
public record class FileFilter(string Name, string[] Exts)
{
    public FilePickerFileType ToFilter()
    {
        return new FilePickerFileType(Name) {
            Patterns = Exts
        };
    }
}

public static class AvaloniaHelper
{
    public static T? FindAncestor<T>(this StyledElement control) where T : AvaloniaObject
    {
        var target = control;

        while (target != null) {
            if (target is T t) {
                return t;
            }

            if (target.Parent == null) {
                return null;
            }

            target = target.Parent;
        }

        return null;
    }

    public static bool HasAncestor(this StyledElement control, StyledElement parent)
    {
        var target = control;

        while (target != null) {
            if (target == control) {
                return true;
            }

            if (target.Parent == null) {
                return false;
            }

            target = target.Parent;
        }

        return false;
    }

    /// <summary>
    /// 切换元素的Classes 
    /// </summary>
    /// <param name="se">UI元素</param>
    /// <param name="add">需要新增的Class</param>
    /// <param name="remove">需要移除的Class</param>
    public static void ToggleClasses(this StyledElement se, string add, params string[] removes)
    {
        foreach (var remove in removes) {
            if (string.IsNullOrWhiteSpace(remove)) continue;
            if (se.Classes.Contains(remove)) {
                se.Classes.Remove(remove);
            }
        }
        if (se.Classes.Contains(add) == false) {
            se.Classes.Add(add);
        }
    }

    public static void SetCheck(this StyledElement se, bool ischecked)
    {
        if (ischecked) {
            if (se.Classes.Contains("unchecked")) {
                se.Classes.Remove("unchecked");
            }
            if (se.Classes.Contains("checked") == false) {
                se.Classes.Add("checked");
            }
        }
        else {
            if (se.Classes.Contains("checked")) {
                se.Classes.Remove("checked");
            }
            if (se.Classes.Contains("unchecked") == false) {
                se.Classes.Add("unchecked");
            }
        }
    }

    public static void SetState(this StyledElement se, bool? yesOrNo)
    {
        if (yesOrNo == true) {
            if (se.Classes.Contains("no")) {
                se.Classes.Remove("no");
            }
            if (se.Classes.Contains("null")) {
                se.Classes.Remove("null");
            }
            if (se.Classes.Contains("yes") == false) {
                se.Classes.Add("yes");
            }
        }
        else if(yesOrNo == false) {
            if (se.Classes.Contains("yes")) {
                se.Classes.Remove("yes");
            }
            if (se.Classes.Contains("null")) {
                se.Classes.Remove("null");
            }
            if (se.Classes.Contains("no") == false) {
                se.Classes.Add("no");
            }
        }
        else {
            if (se.Classes.Contains("no")) {
                se.Classes.Remove("no");
            }
            if (se.Classes.Contains("yes")) {
                se.Classes.Remove("yes");
            }
            if (se.Classes.Contains("null") == false) {
                se.Classes.Add("null");
            }
        }
    }


    public static Color FindColor(this Control ctr, string key)
    {
        if (Application.Current!.TryFindResource(key, out object? value)) {
            if (value is not null && value is Color color) {
                return color;
            }
        }
        throw new KeyNotFoundException(key);
    }

    public static SolidColorBrush FindBrush(this Control ctr, string key)
    {

        if (Application.Current!.TryFindResource(key, out object? value)) {
            if (value is not null && value is SolidColorBrush brush) {
                return brush;
            }
        }
        throw new KeyNotFoundException(key);
    }

    public static T FindResource<T>(this Control ctr, string rawUri) where T : class
    {
        if (rawUri.StartsWith("avares://") == false) {
            throw new ArgumentException("rawUri must start with avares://");
        }

        var asset = AssetLoader.Open(new Uri(rawUri)) ?? throw new EntryPointNotFoundException(rawUri);
        var obj = Activator.CreateInstance(typeof(T), asset);
        if (obj == null) throw new Exception($"Create instance({nameof(T)}) is failed.");
        var t = obj as T;
        if (t == null) throw new Exception($"instance to {nameof(T)} failed");
        return t;
    }

    private static bool PenIsVisible(IPen? pen)
    {
        return pen?.Brush != null && pen.Thickness > 0;
    }

    public static void DrawRectangle(this DrawingContext dc, IBrush? brush, IPen? pen, Rect rect,
        double radiusTopLeft, double radiusTopRight, double radiusBottomRight, double radiusBottomLeft,
       BoxShadows boxShadows = default)
    {
        if (brush == null && !PenIsVisible(pen)) {
            return;
        }

        var max = Math.Max(rect.Width, rect.Height) / 2;
        radiusTopLeft = Math.Min(radiusTopLeft, max);
        radiusTopRight = Math.Min(radiusTopRight, max);
        radiusBottomRight = Math.Min(radiusBottomRight, max);
        radiusBottomLeft = Math.Min(radiusBottomLeft, max);

        dc.DrawRectangle(brush, pen,
            new RoundedRect(rect, radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft),
            boxShadows);
    }

    public static FormattedText CreateFormattedText(this Control control, string content, IBrush brush)
    {
        var typeface = new Typeface(control.GetValue(TextBlock.FontFamilyProperty),
                control.GetValue(TextBlock.FontStyleProperty),
                control.GetValue(TextBlock.FontWeightProperty));
        var size = control.GetValue(TextBlock.FontSizeProperty);
        return new FormattedText(content, Thread.CurrentThread.CurrentCulture, FlowDirection.LeftToRight, typeface, size, brush);
    }

    public static FormattedText CreateFormattedText(this Control control, string content, IBrush brush, double fontsize)
    {
        var typeface = new Typeface(control.GetValue(TextBlock.FontFamilyProperty),
                control.GetValue(TextBlock.FontStyleProperty),
                control.GetValue(TextBlock.FontWeightProperty));
        return new FormattedText(content, Thread.CurrentThread.CurrentCulture, FlowDirection.LeftToRight, typeface, fontsize, brush);
    }

    /// <summary>
    /// 打开文件管理器，并选择一个文件夹
    /// </summary>
    /// <param name="v">窗体中的任一控件</param>
    /// <param name="caption">文件管理器标题</param>
    /// <param name="initFolder">打开管理器的初始目录</param>
    /// <returns></returns>
    public static async Task<string> PickFolder(this Visual v, string caption, string? initFolder = null)
    {
        var win = TopLevel.GetTopLevel(v);
        if (win == null) return string.Empty;

        IStorageFolder? folder = null;
        if (string.IsNullOrWhiteSpace(initFolder) == false) {
            folder = await win.StorageProvider.TryGetFolderFromPathAsync(initFolder);
        }

        var options = new FolderPickerOpenOptions() {
            Title = caption,
            AllowMultiple = false,
            SuggestedStartLocation = folder,
        };

        try {
            var storage = win.StorageProvider;
            var files = await storage.OpenFolderPickerAsync(options);
            if (files.Count == 0) return string.Empty;

            var path = files[0].TryGetLocalPath();
            if (path == null) return string.Empty;

            return path;
        }
        catch (Exception ex) {
            Log.Exception(ex);
            return string.Empty;
        }
    }


    /// <summary>
    /// 打开文件对话框，并选择一个文件, 使用方法如下：<br/>
    /// var path = await v.PickFile("打开媒体文件", [<br/>
    ///        new ("视频文件", ["*.mp4", "*.mov", "*.mkv", "*.avi", "*.wmv"]),<br/>
    ///        new ("音频文件", ["*.mp3", "*.wav", "*.aac", "*.ape", "*.flac", "*.wma"]),<br/>
    ///        new ("所有文件", ["*.*"])]);<br/>
    /// </summary>
    /// <param name="v">窗体中的任一控件</param>
    /// <param name="caption">文件对话框标题</param>
    /// <param name="flters">过滤器组</param>
    /// <returns></returns>
    public static async Task<string> PickFile(this Visual v, string caption, FileFilter[] flters)
    {
        var win = TopLevel.GetTopLevel(v);
        if (win == null) return string.Empty;

        var list = new List<FilePickerFileType>();
        foreach (var f in flters) {
            list.Add(f.ToFilter());
        }
        var options = new FilePickerOpenOptions() {
            Title = caption,
            AllowMultiple = false,
            FileTypeFilter = list,
        };

        try {
            var storage = win.StorageProvider;
            var files = await storage.OpenFilePickerAsync(options);
            if (files.Count == 0) return string.Empty;

            var path = files[0].TryGetLocalPath();
            if (path == null) return string.Empty;

            return path;
        }
        catch (Exception ex) {
            Log.Exception(ex);
            return string.Empty;
        }
    }

    /// <summary>
    /// 打开文件对话框，并选择一个文件保存位置
    /// </summary>
    /// <param name="v">窗体中的任一控件</param>
    /// <param name="caption">文件对话框标题</param>
    /// <param name="flters">过滤器组</param>
    /// <param name="initFileName">缺省文件名</param>
    /// <param name="initFolder">打开管理器的初始目录</param>
    /// <returns></returns>
    public static async Task<string> SaveFile(this Visual v, string caption, FileFilter[] flters, string? initFileName = null, string? initFolder = null)
    {
        var win = TopLevel.GetTopLevel(v);
        if (win == null) return string.Empty;

        var list = new List<FilePickerFileType>();
        foreach (var f in flters) {
            list.Add(f.ToFilter());
        }

        IStorageFolder? folder = null;
        if (string.IsNullOrWhiteSpace(initFolder) == false) {
            folder = await win.StorageProvider.TryGetFolderFromPathAsync(initFolder);
        }

        var options = new FilePickerSaveOptions() {
            Title = caption,
            ShowOverwritePrompt = true,
            FileTypeChoices = list,
            SuggestedFileName = initFileName,
            SuggestedStartLocation = folder
        };

        try {
            var storage = win.StorageProvider;
            var file = await storage.SaveFilePickerAsync(options);
            if (file == null) return string.Empty;

            var path = file.TryGetLocalPath();
            if (path == null) return string.Empty;

            return path;
        }
        catch (Exception ex) {
            Log.Exception(ex);
            return string.Empty;
        }
    }
}
